/////////////////////////////////////////////////////////////////////////
//
// Amuse Engine SDK - system/ccm
// Copyright( c) 2014.  All Rights Reserved
//
// File:		AEChangeControlMgr.h
// Author:		Gianluca Belardelli
// Date:		25/06/2014
//
/////////////////////////////////////////////////////////////////////////
#ifndef _AECHANGECONTROLMGR_H_
#define _AECHANGECONTROLMGR_H_

class AEChangeControlMgr : public AEChangeManagerI
{
// Members
public:

private:
	AEUINT32	m_uiSystemsToBeNotified;
	AEUINT32	m_uiChangesToDist;

protected:
	/// <summary>
	///   Defines a structure used by the CCM to store information about observers
	/// </summary>
	class AEObserverRequest
	{
	// Members
	public:
		AEObserverI	*m_lpObserver;
		AEUINT32	m_uiInterestBits;
		AEUINT32	m_uiObserverIdBits;

	// Methods
	public:
		AEObserverRequest( AEObserverI *lpObserver = 0L,
							AEUINT32 uiInterests = 0,
							AEUINT32 idBits = 0xFFFFFFFF
						) : m_lpObserver( lpObserver ),
							m_uiInterestBits( uiInterests ),
							m_uiObserverIdBits ( idBits ) { }

		bool operator < ( const AEObserverRequest &rhs ) const { return m_lpObserver < rhs.m_lpObserver; }
		bool operator == ( AEObserverI *lpRhs ) const {	return m_lpObserver == lpRhs; }
	};

	typedef AETArray<AEObserverRequest> CCMObserversList;

	/// <summary>
	///   Represents a list of observers extended with cumulative data. 
	/// </summary>
	struct AESubjectInfo
	{
		AESubjectInfo ( void ) : m_lpSubject( 0L ), m_uiInterestBits( 0 ) {}

		/// <summary>
		///   Subject described by this data structure 
		/// </summary>
		AESubjectI*	m_lpSubject;

		/// <summary>
		///   Cumulative interest bits of all observers from this list
		/// </summary>
		AEUINT32	m_uiInterestBits;

		/// <summary>
		///   Observers subscribed to this subject
		/// </summary>
		CCMObserversList	m_arrObserversList;
	};

	typedef AETArray<AEUINT32> IDsList;

	/// <summary>
	///   List of IDs that become free after the subjects associated with them were unregistered
	/// </summary>
	IDsList		m_arrFreeIDsList;

    /// <summary>
    ///   Next ID value to be assigned to a newly registered observer, if free list is empty
    /// </summary>
	AEUINT32	m_uiLastID;

	typedef AETArray<AESubjectInfo> SubjectsList;

	/// <summary>
	///   Vector of observers list indexed by subject ID.
	/// </summary>
	SubjectsList        m_arrSubjectsList;

	struct AENotification
	{
		AENotification ( AESubjectI* lpSubject, AEUINT32 uiChangedBits ) : m_lpSubject( lpSubject ),
													m_uiChangedBits( uiChangedBits ) {}

		AESubjectI	*m_lpSubject;
		AEUINT32	m_uiChangedBits;
	};

public:
	typedef AETArray<AENotification> NotifyList;
protected:
	typedef AETList<NotifyList*>  ListOfNotifyLists;

	/// <summary>
	///   Cross-thread list of notification lists. Accessed only from the main thread.
	/// </summary>
	ListOfNotifyLists   m_lstNotifyLists;

	/// <summary>
	///   TLS slot that store pointers to NotifyListInfo values, containing pointer 
	///   to thread local notification lists with fast search ability and .
	/// </summary>
	static AE_THREAD_LOCAL(NotifyList *)	m_tlsNotifyList;

	struct AEMappedNotification
	{
		AEMappedNotification ( AEUINT32 uiID, AEUINT32 uiChangedBits ) : m_uiSubjectID( uiID ),
														m_uiChangedBits( uiChangedBits ) {}

		AEUINT32	m_uiSubjectID;
		AEUINT32	m_uiChangedBits;
	};

	typedef AETArray<AEMappedNotification> MappedNotifyList;

	/// <summary>
	///   Cumulative list of notifications posted during the last execution stage.
	///   Used during distribution stage only.
	/// </summary>
	MappedNotifyList    m_arrCumulativeNotifyList;

	/// <summary>
	///   Used while generating cumulative list during the distribution stage.
	/// </summary>
	IDsList             m_arrIndexList;

	AEITaskManager		*m_lpTaskManager;

	AECriticalSection	m_csUpdate;

// Methods
private:
	static void _initThreadLocalData( void *lpArg );
	static void _freeThreadLocalData( void *lpArg );
	static void _distributionCallback( void *lpParam, AEUINT32 uiBegin, AEUINT32 uiEnd );

	AEUINT32 _removeSubject ( AESubjectI *lpSubject );
	void _distributeRange ( AEUINT32 uiBegin, AEUINT32 uiEnd );

	NotifyList &_getNotifyList( void );

public:
	AEChangeControlMgr( void );
	virtual ~AEChangeControlMgr( void );

	// Implementazione dei metodi di AEChangeManagerI
	virtual AEUINT32 Register( AESubjectI *lpInSubject, AEUINT32 uiInIntrestBits, AEObserverI *lpInObserver, AEUINT32 uiObserverIdMask = 0xFFFFFFFF );

    virtual AEUINT32 Unregister( AESubjectI *lpInSubject, AEObserverI *lpInObserver );

    virtual AEUINT32 DistributeQueuedChanges( AEUINT32 uiSystemsToBeNotified = 0xFFFFFFFF,
												AEUINT32 uiChangesToDist = 0xFFFFFFFF );

	// Implementazione dei metodi di AEObserverI
	virtual AEUINT32 ChangeOccurred( AESubjectI *lpSubject, AEUINT32 uiChangeTypeMask );

	// Must be called before any parallel execution starts (that is 
	// before changes start being accumulated in thread local lists), 
	// but after the task manager has been initialized
	AEUINT32 SetTaskManager( AEITaskManager *lpTaskMgr );

	// Must be called before the previously set task manager has been shut down
	void ResetTaskManager( void );
};

#endif // _AECHANGECONTROLMGR_H_
